package com.pkk.cloud.support.oauth2.center.runner.service;

import com.github.pagehelper.util.StringUtil;
import com.pkk.cloud.support.admin.oauth2.api.admin.AdminApi;
import com.pkk.cloud.support.admin.oauth2.api.common.req.PenguinSysUserIdReq;
import com.pkk.cloud.support.admin.oauth2.api.common.resp.BasicUserInfo;
import com.pkk.cloud.support.admin.oauth2.api.common.resp.Oauth2UserDetails;
import com.pkk.cloud.support.admin.oauth2.api.common.resp.PenguinSysUserInfo;
import com.pkk.cloud.support.admin.oauth2.api.common.resp.PenguinSysUserAuthorizeResp;
import com.pkk.cloud.support.oauth2.common.constant.OauthConstant;
import com.pkk.cloud.support.oauth2.config.integration.IntegrationAuthentication;
import com.pkk.cloud.support.oauth2.config.integration.IntegrationAuthenticationContext;
import com.pkk.cloud.support.oauth2.config.integration.authenticator.IntegrationAuthenticator;
import com.pkk.components.moudle.oauth2.AuthServiceException;
import com.pkk.components.rpc.common.code.AuthoritiesCode;
import com.pkk.components.rpc.common.code.CommonCode;
import com.pkk.components.rpc.request.CommonRequest;
import com.pkk.components.rpc.request.RequestHeader;
import com.pkk.components.rpc.response.CommonResponse;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
 * @description: 集成用户信息服务
 * <p>
 * 获取基础用户的信息
 * @author: peikunkun
 * @create: 2020-03-18 11:14
 **/
@Slf4j
@Service
public class IntegrationUserDetailsService implements UserDetailsService {

  @Autowired
  private StringRedisTemplate stringRedisTemplate;
  @Resource
  private AdminApi adminApi;
  //认证器列表,进行认证的操作
  private List<IntegrationAuthenticator> authenticators;

  @Autowired(required = false)
  public void setIntegrationAuthenticators(List<IntegrationAuthenticator> authenticators) {
    this.authenticators = authenticators;
  }

  /**
   * @Description: 获取用户信息依据用户名称
   * @Param: username
   * @return: com.pkk.cloud.support.admin.oauth.api.common.resp.Oauth2UserDetails
   * @Author: peikunkun
   * @Date: 2020/3/18 0018 下午 4:44
   */
  @Override
  public Oauth2UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    IntegrationAuthentication integrationAuthentication = IntegrationAuthenticationContext.get();
    //判断是否是集成登录
    if (integrationAuthentication == null) {
      integrationAuthentication = new IntegrationAuthentication();
    }
    integrationAuthentication.setUsername(username);
    //进行认证获取用户用户信息操作
    PenguinSysUserInfo sysUserInfo = this.authenticate(integrationAuthentication);

    BasicUserInfo myUserInfo = new BasicUserInfo();
    BeanUtils.copyProperties(sysUserInfo, myUserInfo);

    //设置授权信息操作
    User user = this.setAuthorize(sysUserInfo);
    //返回Oauth2UserDetails的信息
    return new Oauth2UserDetails(myUserInfo, user);

  }

  /**
   * @Description: 设置授权信息
   * @Param: sysUserInfo
   * @return: org.springframework.security.core.userdetails.User
   * @Author: peikunkun
   * @Date: 2020/3/18 0018 下午 2:03
   */
  public User setAuthorize(PenguinSysUserInfo sysUserInfo) {
    //@formatter:off
    //用户授权基本信息的请求
    CommonRequest<PenguinSysUserIdReq> reqCommonRequest = new CommonRequest<>(new RequestHeader(), PenguinSysUserIdReq.builder().userId(sysUserInfo.getId()).build());
    CommonResponse<PenguinSysUserAuthorizeResp> authorize = this.adminApi.getAuthorize(reqCommonRequest);
    if (null == authorize || !authorize.getHeader().getCode().equals(CommonCode.SUCCESS.getCode()) || null == authorize.getBody()) {
      throw new AuthServiceException(AuthoritiesCode.GET_USER_RESOURCE_ERROR);
    }

    //设置并刷新资源缓存
    List<GrantedAuthority> grantedAuthorities = this.convertToRoles(sysUserInfo.getUsername(), authorize.getBody().getRoles());
    this.refreshResourceCache(sysUserInfo.getUsername(), authorize.getBody().getResources());

    User user = new User(sysUserInfo.getUsername(),
        sysUserInfo.getPassword(), sysUserInfo.isEnabled(),
        sysUserInfo.isAccountNonExpired(), sysUserInfo.isCredentialsNonExpired(),
        sysUserInfo.isAccountNonLocked(), grantedAuthorities);
    //@formatter:on
    return user;
  }

  /**
   * @Description: 转换为角色
   * @Param: username
   * @Param roles
   * @return: java.util.List<org.springframework.security.core.GrantedAuthority>
   * @Author: peikunkun
   * @Date: 2020/3/18 0018 下午 2:04
   */
  private List<GrantedAuthority> convertToRoles(String username, Collection<String> roles) {
    List<GrantedAuthority> authorities = new ArrayList();
    roles.stream().forEach(e -> {
      if (!StringUtil.isEmpty(e)) {
        authorities.add(new SimpleGrantedAuthority(e));
      }
    });
    return authorities;
  }

  /**
   * @Description: 清除resource 缓存
   * @Param: username
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/20 0020 下午 4:15
   */
  public void removeResourceCache(String username) {
    // 清除 Redis 中用户的资源信息
    stringRedisTemplate.delete(OauthConstant.getUserResourceKey(username));
  }

  /**
   * @Description: 更新resource 缓存[删除并重新设置资源的接口地址信息],这里会设置到redis中,后续用于鉴权用
   * @Param: username
   * @Param resources
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/20 0020 下午 4:15
   */
  private void refreshResourceCache(String username, Collection<String> resources) {
    // 清除 Redis 中用户的set资源信息
    stringRedisTemplate.delete(OauthConstant.getUserResourceKey(username));
    resources.stream().forEach(r -> {
      if (!StringUtil.isEmpty(r)) {
        //存储资源到redis
        stringRedisTemplate.opsForSet().add(OauthConstant.getUserResourceKey(username), r);
      }
    });
  }


  /**
   * @Description: 根据不同的授权类型进行获取用户基础信息
   * @Param: integrationAuthentication
   * @return: com.pkk.cloud.support.admin.oauth2.api.common.resp.PenguinSysUser
   * @Author: peikunkun
   * @Date: 2020/3/20 0020 下午 4:16
   */
  private PenguinSysUserInfo authenticate(IntegrationAuthentication integrationAuthentication) {
    if (this.authenticators != null) {
      for (IntegrationAuthenticator authenticator : authenticators) {
        if (authenticator.support(integrationAuthentication)) {
          return (PenguinSysUserInfo) authenticator.authenticate(integrationAuthentication);
        }
      }
    }
    return null;
  }
}
